/// <reference path = "./ShaderComponent.ts" />

module lcc$render {

const {ccclass, property, menu } = cc._decorator;

//@ts-ignore
let gfx = cc.gfx;

let _temp_color = cc.color();

@ccclass("lcc$render.ShaderColor")
@menu("i18n:lcc-render.menu_component/ShaderColor")
export class ShaderColor extends ShaderComponent {

    _tag:string = "color";
    
	@property({
		type :cc.Enum(ValueType)
	})
	_colorType:ValueType = ValueType.SINGLE;
	@property({
		type :cc.Enum(ValueType),
		tooltip : "颜色类型"
	})
	get colorType(){
		return this._colorType;
	}
	set colorType(value:ValueType){
		if(this._colorType != value){
			this._colorType = value;
			if(value == ValueType.ARRAY){
                this._colorVar.type == VariableType.UNIFORM;
                this._colorVar._typesel = false;
            }else{
                this._colorVar._typesel = true;
            }
		}
	}

	@property(VariableConfig)
	_colorVar:VariableConfig = new VariableConfig(
		true, VariableType.UNIFORM, 
		"ATTR_COLOR", false, "a_color", "UNIF_COLOR", false, "u_color");
	@property({
		type : VariableConfig,
		tooltip : "颜色变量"
	})
	get colorVar(){
		return this._colorVar;
	}
	set colorVar(value:VariableConfig){
        this._colorVar = value;
        this.onUpdateColors();
		this.onRenderUpdateMaterial();
		this.node.emit("shader_update_attribute");
		//Editor.log("colorVar");
    }
    
    @property([cc.Color])
	_colors:cc.Color[] = [ cc.Color.WHITE ];
	
	@property()
	_useNodeColor:boolean = false;
	@property({
        tooltip : "使用节点的颜色"
	})
	get useNodeColor(){
		return this._useNodeColor;
	}
	set useNodeColor(value:boolean){
		if(this._useNodeColor != value){
            this._useNodeColor = value;
            if(value){
                this.onNodeColorChanged();
            }else{
                this.onUpdateColors();
            }
		}
	}

	@property({
		visible (){
			return this._colorType == ValueType.SINGLE && this._colorVar.type == VariableType.UNIFORM;
		},
		type : cc.Color,
		tooltip : "颜色值"
	})
	get color(){
		return this._colors[0];
	}
	set color(value:cc.Color){
        this._useNodeColor = false;
        this._colors[0] = value;
		this.onUpdateColors();
	}
    
	@property({
		visible (){
			return this._colorType == ValueType.ARRAY || this._colorVar.type == VariableType.ATTRIBUTE;
		},
		type : [cc.Color],
		tooltip : "颜色数组/颜色顶点数组"
	})
	get colors(){
		return this._colors;
	}
	set colors(value:cc.Color[]){
        this._useNodeColor = false;
		this._colors = value;
		this.onUpdateColors();
	}

	/**
	 * 颜色偏移
	 */
    private _colorOffset:number = 0;

    onLoad(){
        this.node.on("render_shape_checked", this.onRenderShapeChecked, this);
        this.node.on("render_update_material", this.onRenderUpdateMaterial, this);
		this.node.on("render_update_attribute", this.onRenderUpdateAttribute, this);
		this.node.on("render_update_vertex", this.onRenderUpdateRenderData, this);
		this.node.on(cc.Node.EventType.COLOR_CHANGED, this.onNodeColorChanged, this);
    }

    onDestroy(){
		this.node.targetOff(this);
    }
    
	onEnable(){
		this.onNodeColorChanged();
        this.onStateUpdate(true);
	}

	onDisable(){
        this.onStateUpdate(false);
    }

    private onStateUpdate(enable:boolean){
		this.node.emit("shader_update_tag");
		if(this._useNodeColor){
			this.onNodeColorChanged();
		}else{
			this.onUpdateColors();
		}
    }
    
	private onNodeColorChanged(){
		if(this._useNodeColor){
            this.onUpdateColors();
		}
	}

    /**
     * 检查形状
     */
    private onRenderShapeChecked(){
        if(this._colorVar.type == VariableType.ATTRIBUTE){
            this.onUpdateColors();
        }
    }
	
	/**
	 * 检查颜色
	 */
	private onUpdateColors(){
		if(this.enabled){
            let dcolor = cc.Color.WHITE;
            if(this._colors.length <= 0){
                this._colors = [ dcolor.clone() ];
            }
            if(this._colorVar.type == VariableType.ATTRIBUTE){
                let rsys = this.getComponent(RenderSystem);
                if(rsys){
                    let vc = rsys._verticesCount;
                    if(this._colors.length < vc){
                        let colors = this._colors;
                        for(let i = 0; i < vc; i++){
                            let color = colors[i];
                            if(color == null){
                                colors[i] = dcolor.clone();
                            }
                        }
                    }
                    this._colors.length = vc;
                }
            }else if(this._colorType == ValueType.SINGLE){
                this._colors.length = 1;
            }
            if(this._useNodeColor){
                let ncolor = this.node.color;
                for(let i = 0, l = this._colors.length; i < l; i++){
                    this._colors[i] = ncolor.clone();
                }
            }
            if(this._colorVar.type == VariableType.ATTRIBUTE){
                this.node.emit("shader_update_attribute");
            }else{
                this.onRenderUpdateMaterial();
            }
        }
	}
    
	private premultiplyAlpha(color:cc.Color){
		// @ts-ignore
		cc.Color.premultiplyAlpha(_temp_color, color);
		// @ts-ignore
		return _temp_color._val;
	}

	/**
	 * 获得属性颜色
	 * @param comp 
	 */
	private getAttributeColors(comp:RenderSystem){
		let premultiply = comp._srcBlendFactor === cc.macro.BlendFactor.ONE;
		let colors:number[] = [];
		if(this._colorVar.type === VariableType.ATTRIBUTE){
            for (let i = 0, l = this._colors.length; i < l; i++){
                let c = this._colors[i];
                // @ts-ignore
                colors.push(premultiply ? this.premultiplyAlpha(c) : c._val);
            }
        }
		return colors;
	}

    /**
     * 获得常量颜色
     * @param comp 
     */
	private getUniformColors(comp:RenderSystem){
		let premultiply = comp._srcBlendFactor === cc.macro.BlendFactor.ONE;
        let colors:cc.Vec4[] = [];
        if(this._colorVar.type === VariableType.UNIFORM){
            for (let i = 0, l = this._colors.length; i < l; i++){
                let c = this._colors[i];
                if(premultiply){
                    //@ts-ignore
                    cc.Color.premultiplyAlpha(_temp_color, c);
                    c = _temp_color;
                }
                colors.push(new cc.Vec4(c.r / 255, c.g / 255, c.b / 255, c.a / 255));
            }
        }
		return colors;
	}

    private onRenderUpdateMaterial(comp:RenderSystem = this.getComponent(RenderSystem)) {
		if(comp){
			// make sure material is belong to self.
			//@ts-ignore
			let material = comp._materials[0];
			if (material) {
				if (this.checkMaterialMacro(material, this._colorVar.unifMacro)) {
					if(this.enabled && this._colorVar.type === VariableType.UNIFORM){
                        this.defineMaterialMacro(material, this._colorVar.unifMacro, true);
                        let colors = this.getUniformColors(comp);
                        if(this._colorType == ValueType.SINGLE){
                            material.setProperty(this._colorVar.unifName, colors[0]);
                        }else{ // ARRAY
                            let _colors:number[] = [];
                            for(let i = 0, l = colors.length; i < l; i++){
                                let c = colors[i];
                                _colors.push(c.x, c.y, c.z, c.w);
                            }
                            material.setProperty(this._colorVar.unifName, new Float32Array(colors.length * 4));
                            material.setProperty(this._colorVar.unifName, _colors);
                        }
						//Editor.log("onRenderUpdateMaterial", this.getUniformColor(comp));
					}else{
						this.defineMaterialMacro(material, this._colorVar.unifMacro, false);
					}
					//Editor.log("onRenderUpdateMaterial", this.enabled);
				}
			}
		}
    }
	
	private onRenderUpdateAttribute(comp:RenderSystem = this.getComponent(RenderSystem)){
		if(comp){
			// @ts-ignore
			let material = comp._materials[0];
			if (material) {
				if (this.checkMaterialMacro(material, this._colorVar.attrMacro)) {
					if(this.enabled && this._colorVar.type === VariableType.ATTRIBUTE){
						this.defineMaterialMacro(material, this._colorVar.attrMacro, true);
						this._colorOffset = comp.addVertexAttribute({ 
							name: this._colorVar.attrName, 
							type: gfx.ATTR_TYPE_UINT8, 
							num: 4, 
							normalize: true 
						}, 1);
						//Editor.log("onRenderUpdateAttribute", this._colorOffset);
					}else{
						this.defineMaterialMacro(material, this._colorVar.attrMacro, false);
						this._colorOffset = 0;
					}
					//Editor.log("onRenderUpdateAttribute", this.enabled);
				}
			}
		}
	}

	private onRenderUpdateRenderData(comp:RenderSystem = this.getComponent(RenderSystem)){
        if(comp && this.enabled){
			//Editor.log("onRenderUpdateRenderData");
			if(this._colorOffset > 0){
				//@ts-ignore
				let assembler = comp._assembler;
				let uintVerts = assembler.renderData.uintVDatas[0];
				if (uintVerts) {
					let colors = this.getAttributeColors(comp);
					let clen = colors.length;
					let colorOffset = this._colorOffset;
					let floatsPerVert = assembler.floatsPerVert;
					for (let i = colorOffset, l = uintVerts.length, j = 0; i < l; i += floatsPerVert, j++) {
						uintVerts[i] = j < clen ? colors[j] : colors[0];
					}
				}
			}
        }
	}
}

}
